#include <iostream>
#include <assert.h>
#include <crtdbg.h>
#include "SfMemoryAllocManager.h"
#include "SfMemoryObject.h"
#include "SfRefObject.h"
#include "SfSmartPtr.h"
#include "SfGameObject.h"
#include "SfGameObjectManager.h"
#include "SfGameObjectComponent.h"
#include "SfMessageDispatcher.h"

using namespace Sunflower;

////////////////////////////////////////////////////////////////////////////
////base
////////////////////////////////////////////////////////////////////////////
//// memory manage
//class MemoryObject;
//class MemoryAllocator;
//class MemoryAllocManager;
//
//// base classes
//class RefObject;
//class SmartPointer;
//class Object;
//
//// rtti
//class Rtti;
//
//// serialization
//class ISerializable;
//class ISerializer;
//class BinarySerializer;
//class TextSerializer;
//class XmlSerializer;
//class Stream;
//class BinaryStream;
//class TextStream;
//class XmlStream;
//
////dynamic creation
//class ObjectFactory;
//
////
////communication between game objects
//class Telegram;
//class Message;
//class MessageDispatcher;
//class MessageReciever;
//
////////////////////////////////////////////////////////////////////////////
////service
////////////////////////////////////////////////////////////////////////////
//class GameService;
//class GameServiceProvider;
//
////////////////////////////////////////////////////////////////////////////
////game application
////////////////////////////////////////////////////////////////////////////
//class Game;
//
////////////////////////////////////////////////////////////////////////////
//// game director
////////////////////////////////////////////////////////////////////////////
//class GameDirector;
//
////////////////////////////////////////////////////////////////////////////
////game object
////////////////////////////////////////////////////////////////////////////
////
//// main class
//class GameObject;
//class GameObjectManager;
////
//// goc
//class GameObjectComponent;
//
////////////////////////////////////////////////////////////////////////////
//// game state machine.
////////////////////////////////////////////////////////////////////////////
//class GameState;
//class GameStateMachine;

//////////////////////////////////////////////////////////////////////////
//cache manage
//////////////////////////////////////////////////////////////////////////
class ManagedCache;
class CacheManager;

//////////////////////////////////////////////////////////////////////////
//begin test for MemoryObject

class TestObject : public RefObject
{
public:
	TestObject()
	{
		std::cout << "TestObject::TestObject()" << std::endl;
	}

	~TestObject()
	{
		std::cout << "TestObject::~TestObject()" << std::endl;
	}

	void Func1()
	{
		std::cout << "TestObject::Func1" << std::endl;
	}

	void Func2()
	{
		std::cout << "TestObject::Func2" << std::endl;
	}
};

SfDeclareSmartPtr(TestObject)

class MyMemoryTracker : public MemoryTracker
{
public:
	//
	//Summary:
	//	call-back on memory allocated.
	virtual void OnMemoryAlloc(MemoryAllocInfo* mai)
	{
		std::string str;
		ParseMai(str, mai);

		std::cout << "Memory Alloc: " << str << std::endl;
	}

	//
	//Summary:
	//	call-back on memory freed.
	virtual void OnMemoryFree(MemoryAllocInfo* mai)
	{
		std::string str;
		ParseMai(str, mai);

		std::cout << "Memory Free: " << str << std::endl;
	}

protected:
	//
	//Summary:
	//	print mai to std output device.
	void ParseMai(std::string& str, MemoryAllocInfo* mai)
	{
		char buffer[1024];
		sprintf_s(buffer, 1024, "<Mai Addr=0x%x RequestSize=%d FileName=%s LineNum=%d FuncName=%s />",
			mai->Addr, mai->RequestSize, mai->FileName.c_str(), mai->LineNum, 
			mai->FuncName.c_str());

		str = buffer;
	}
};

void LinkMessageObjects(Message& msg)
{
	GameObjectManager* gom = GameObjectManager::GetInstancePtr();
	
	if (SfIsValidPtr(gom))
	{
		// sender
		if (SfIsNullPtr(msg.Sender.ObjectPtr))
		{
			msg.Sender.ObjectPtr = 
				gom->GetGameObjectByGoid(msg.Sender.Goid);
		}

		//receiver
		if (SfIsNullPtr(msg.Reciever.ObjectPtr))
		{
			msg.Reciever.ObjectPtr = 
				gom->GetGameObjectByGoid(msg.Reciever.Goid);
		}
	}
}

void PrintMessage(const Message& msg)
{
	Message saved_msg(msg);
	LinkMessageObjects(saved_msg);

	std::string senderName = "null";

	if (SfIsValidPtr(saved_msg.Sender.ObjectPtr))
	{
		senderName = saved_msg.Sender.ObjectPtr->GetGoName();
	}

	std::string recieverName = "null";

	if (SfIsValidPtr(saved_msg.Reciever.ObjectPtr))
	{
		recieverName = saved_msg.Reciever.ObjectPtr->GetGoName();
	}

	std::cout << "<Message " 
		<< "MsgID=" << saved_msg.MsgID 
		<< " Sender=" << senderName
		<< " Reciever=" << recieverName 
		<< " TimeStamp=" << saved_msg.TimeStamp 
		<< " Argument=" << (unsigned int)(saved_msg.Argument.GetRawPtr())
		<< ">";
}

class TestGameObject : public GameObject
{
public:
	TestGameObject(){}
	TestGameObject(const char* name)
		: GameObject(name)
	{

	}
	//
	//Summary:
	//	override IMessageHandler::OnMessage() to handle message.
	virtual MessageHandleResult OnMessage(const Message& msg)
	{
		std::cout << "=======================================" << std::endl;
		std::cout << "TestGameObject::OnMessage" << msg.MsgID << std::endl;
		PrintMessage(msg);
		std::cout << std::endl;
		std::cout << "=======================================" << std::endl;
		return MessageHandleResultPass;
	}
};



class TestGoc : public GameObjectComponent
{
public:
	TestGoc(){}
	TestGoc(const char* name)
		: GameObjectComponent(name)
	{
	}

};

class MessageArugment_Test : public MessageArgument
{
public:
	virtual void DeleteThis()
	{
		std::cout << "MessageArugment_Test::DeleteThis" << std::endl;
		MessageArgument::DeleteThis();
	}
	int Param1;
};

MemoryAllocManager g_MemoryAllocManager;
MyMemoryTracker g_Mat;
GameObjectManager g_Gom;
MessageDispatcher g_md;


MessageHandleResult ProcUnhandledMessage(const Message& msg)
{
	std::cout << "Unhandled message :" << std::endl;
	PrintMessage(msg);

	return MessageHandleResultDone;
}

void InitTest_MemoryObject()
{
	g_MemoryAllocManager.SetTracker(&g_Mat);

	MessageDispatcher::MessageHandleDelegate d(ProcUnhandledMessage);
	g_md.SetUnhandledMessageFilter(d);
	//SfCreateSharedSingleton(MemoryAllocManager);
}

void DeinitTest_MemoryObject()
{
	MessageDispatcher::MessageHandleDelegate d;
	g_md.SetUnhandledMessageFilter(d);

	SfAssert(g_Mat.GetUsingCount() == 0 && "memory leak");
	g_MemoryAllocManager.SetTracker(SfNull);
	//SfDetroySharedSingleton(MemoryAllocManager);
}

void TestMain()
{
	//TestObjectPtr test1 = SfNew TestObject();
	//test1->Func1();

	////SfDelete test1;

	//TestObjectPtr test11 = (TestObject*)test1;
	//(*test11).Func1();

	////test1 = SfNull;
	////test11 = SfNull;

	//// 
	//TestObjectPtr test2 = SfNew TestObject();
	//test2->Func2();

	// game objects.
	GameObjectPtr go1 = SfNew TestGameObject("go1");
	GameObjectComponent* goc1 = SfNew TestGoc("goc1");
	GameObjectComponent* goc2 = SfNew TestGoc("goc2");

	go1->AddGoc(goc1);
	go1->AddGoc(goc2);

	GameObjectPtr go2 = SfNew TestGameObject("go2");
	go2->AddGoc(goc1);

	//GameObjectManager* gom = GameObjectManager::GetInstancePtr();
	//go2 = SfNull;

	MessageDispatcher* md = MessageDispatcher::GetInstancePtr();

	//md->BroadcastMessage(1, go2, SfNew MessageArugment_Test);
	//md->PostMessage(2, go1, go2, SfNew MessageArugment_Test);

	md->PostDelayedMessage(1, go1, go2, SfNew MessageArugment_Test, 0);
	md->BroadcastDelayedMessage(2, SfNull, SfNew MessageArugment_Test, 0);

	md->UpdateTime(0);
	md->UpdateAllDelayedMessages();

	int i = 0;
}

void RunTest_MemoryObject()
{
	InitTest_MemoryObject();

	TestMain();
	//test2 = SfNull;
	//SfDelete test2;

	DeinitTest_MemoryObject();
}

//end test for MemoryObject
//////////////////////////////////////////////////////////////////////////

inline void EnableMemLeakCheck()
{
	_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);
}

//////////////////////////////////////////////////////////////////////////
// application entry
//////////////////////////////////////////////////////////////////////////
void main()
{
	EnableMemLeakCheck();
	 _set_error_mode(_OUT_TO_MSGBOX);
	 
	RunTest_MemoryObject();
	getchar();
}